package src.mcpichess.control;
import static pi.Vec.xyz;

import java.util.ArrayList;

import pi.Block;
import pi.Color;
import pi.Minecraft;
import pi.Vec;
import pi.event.BlockHitEvent;
import src.mcpichess.pieces.Bishop;
import src.mcpichess.pieces.Empty;
import src.mcpichess.pieces.King;
import src.mcpichess.pieces.Knight;
import src.mcpichess.pieces.Pawn;
import src.mcpichess.pieces.Piece;
import src.mcpichess.pieces.Queen;
import src.mcpichess.pieces.Rook;
import src.mcpichess.uci.Move;
import src.mcpichess.control.UCI;

public class Main {
	

	private static final boolean makeCeiling = true;
	private static final boolean makeWalls = true;
	private static final int CAGEHEIGHT = 7;
	public final int px = 0;
	public final int py = 30;
	public final int pz = 0;
	Block p1Block = Block.id(155);
	Block p2Block = Block.OBSIDIAN;
	Block[] pBlocks = {Block.AIR, p1Block, p2Block};
	Block possibleMoveBlock = Block.LAPIS_LAZULI_BLOCK;
	Block lastMoveBlock = Block.STONE;
	int[] lastMoveFrom;
	int[] lastMoveTo;
	Minecraft mc;
	Vec playerpos;
	public HitListener listener;
	private boolean clickEnabled = false;
	
	int origX;
	int origY;
	int origZ;
	int xLoc = 0;
	int zLoc = 0;
	int oldX;
	int oldZ;
	public int turn = 1;
	
	public Empty empty;
	public Pawn pawn;
	public Rook rook;
	public Knight knight;
	public Bishop bishop;
	public Queen queen;
	public King king;
	
	public UCI uci;
	
	public ArrayList<int[]> drawnTiles;
	public ArrayList<int[]> availableMoves;
	Color[][] colors;
	
	String[][] board = {				//Array of pieces locations on the board. The first number represents the player, the second the piece
			{"12","13","14","15","16","14","13","12"},
			{"11","11","11","11","11","11","11","11"},
			{"00","00","00","00","00","00","00","00"},
			{"00","00","00","00","00","00","00","00"},
			{"00","00","00","00","00","00","00","00"},
			{"00","00","00","00","00","00","00","00"},
			{"21","21","21","21","21","21","21","21"},
			{"22","23","24","25","26","24","23","22"},
	};
	
	String[][] previousBoard = UCI.copyBoard(board);
	private boolean boardExists;
	
	public int getPlayerAt(int[] loc){
		try{
			String rawStr = board[loc[1]][loc[0]];
			return Integer.parseInt(rawStr.substring(0,1));
		}catch(Exception e){
			e.printStackTrace();
			return 0;
		}
	}
	
	public Piece getPieceAt(int[] loc){
		try{
			String rawStr = board[loc[1]][loc[0]];
			return intToPiece(Integer.parseInt(rawStr.substring(1, 2)));
		}catch(Exception e){
			e.printStackTrace();
			return empty;
		}
	}
	
	public Piece intToPiece(int i){			//Hooks you up with a Piece object correlating with an int 0-6
		Piece returnPiece = empty;
		switch (i){
			case(1):{
				returnPiece = pawn;
				break;
			}
			case(2):{
				returnPiece = rook;
				break;
			}
			case(3):{
				returnPiece = knight;
				break;
			}
			case(4):{
				returnPiece = bishop;
				break;
			}
			case(5):{
				returnPiece = queen;
				break;
			}
			case(6):{
				returnPiece = king;
				break;
			}
		}
		return returnPiece;
	}
	
	public static int[] pair(int a,int b){			//returns an int[] of the two ints
		int[] ab = {a,b};
		return ab;
	}
	
	public void buildPiece(int[] loc, Piece piece, Block material, int player){		//x and z are ints from 0-7
		int box = origX + loc[0]*5+1; 		//Build Origin points: the top-left-back corner of the piece
		int boy = origY + 5;
		int boz = origZ + loc[1]*5+1;
		if (!(loc[0] < 0 || loc[0] > 7 || loc[1] < 0 || loc[1] > 7)){		//check to see if the given location is inbounds
			if (player == 1){					//Draw normally
				for (int bx = 0; bx <= 2; bx++){
					for (int bz = 0; bz <= 2; bz++){
						for (int by = 0; by <= 4; by++){
							int read = piece.getBlockAt(bx, by, bz);
							if (read == 0){
								mc.setBlock(box+bx, boy-by, boz+bz, Block.AIR);
							}else{
								mc.setBlock(box+bx, boy-by, boz+bz, material);
							}
						}
					}
				}
			}
			
			if (player == 2){					//Mirror z-axis
				for (int bx = 0; bx <= 2; bx++){
					for (int bz = 0; bz <= 2; bz++){
						for (int by = 0; by <= 4; by++){
							int read = piece.getBlockAt(bx, by, bz);
							if (read == 0){
								mc.setBlock(box+bx, boy-by, boz+(bz*-1+2), Block.AIR);
							}else{
								mc.setBlock(box+bx, boy-by, boz+(bz*-1+2), material);
							}
						}
					}
				}
			}
		}
	}
	
	public void erasePiece(int[] loc){		//x and z are ints from 0-7
		int box = origX + loc[0]*5+1; 		//Build Origin points: the top-left-back corner of the piece
		int boy = origY + 5;
		int boz = origZ + loc[1]*5+1;
		if (!(loc[0] < 0 || loc[0] > 7 || loc[1] < 0 || loc[1] > 7)){		//check to see if the given location is inbounds
			for (int bx = 0; bx <= 2; bx++){
				for (int bz = 0; bz <= 2; bz++){
					for (int by = 0; by <= 4; by++){
						mc.setBlock(box+bx, boy-by, boz+bz, Block.AIR);
					}
				}
			}
		}
	}
	
	public void drawOutline(int[] loc, Block block){
		
		if (!(loc[0] < 0 || loc[0] > 7 || loc[1] < 0 || loc[1] > 7)){		//check to see if the given location is inbounds
			if (!(block.equals(Block.wool(Color.WHITE)) || block.equals(Block.wool(Color.BLACK)) || block.equals(lastMoveBlock))){
				/* add it to a list of drawn tiles */
				drawnTiles.add(loc);
			}else if (block.equals(Block.wool(Color.WHITE)) || block.equals(Block.wool(Color.BLACK))){
				drawnTiles.remove(loc);
			}
			for (int a = 0; a <=4; a++){		
				mc.setBlock(origX+loc[0]*5+a,origY,origZ+loc[1]*5+0,block);		//draw new square
				mc.setBlock(origX+loc[0]*5+a,origY,origZ+loc[1]*5+4,block);
				mc.setBlock(origX+loc[0]*5+0,origY,origZ+loc[1]*5+a,block);
				mc.setBlock(origX+loc[0]*5+4,origY,origZ+loc[1]*5+a,block);
			}
		}
	}
	

	public void clearTiles(){
		@SuppressWarnings("unchecked")
		ArrayList<int[]> cloney = (ArrayList<int[]>) drawnTiles.clone();	//duplicates drawnTiles to prevent concurrent modification
		for (int[] i : cloney){
			Color c = getBoardColor(i);
			drawOutline(i, Block.wool(c));
		}
		drawnTiles.clear();
	}
	
	public void doSelect(BlockHitEvent event){
		oldX = xLoc;
		oldZ = zLoc;
		xLoc = (event.position.x - origX)/5;		//0-7 Coordinates of event
		zLoc = (event.position.z - origZ)/5;

		if (!mc.getBlock(event.position).equals(Block.GLASS)){
			int p = getPlayerAt(pair(xLoc,zLoc));
			if (((oldX != xLoc) || (oldZ != zLoc))){		//If the block isn't already selected, move the outline to the new square
				
				if (!isAvailable(pair(xLoc, zLoc))){	//check if you clicked on an available move
					if (p == turn || p == 0){
						/*Change selected tile*/
						clearTiles();
						availableMoves.clear();
						drawOutline(pair(xLoc, zLoc), Block.GOLD_BLOCK);		//draw new square 
						Color replaceColor = getBoardColor(pair(oldX, oldZ));
						drawOutline(pair(oldX, oldZ), Block.wool(replaceColor));	//replace old square
						Piece sPiece = this.getPieceAt(pair(xLoc, zLoc));
						if (sPiece != empty){		//if there's a piece here, show its moves.
							availableMoves  = sPiece.getMoves(xLoc, zLoc, this.getPlayerAt(pair(xLoc, zLoc)));
							for (int[] move : availableMoves){
								drawOutline(move, possibleMoveBlock);
							}
						}
					}
				}else{
					try{
						/*Move selected piece*/
						Move move = new Move(oldX, oldZ, xLoc, zLoc);	
						movePiece(move);
						movePiece(uci.sendAndReceiveMove(move, previousBoard)); 
						previousBoard = UCI.copyBoard(board);
						clearTiles();
						availableMoves.clear();
						xLoc = oldX;
						zLoc = oldZ;
						drawOutline(pair(oldX, oldZ), Block.GOLD_BLOCK);
						if (uci.isInCheck()){
							mc.postToChat("You are in check.");
						}
						if (uci.isCheckmate()){
							mc.postToChat("You lose!");
						}
					}catch(Exception e){
						System.out.println("Illegal move caught and fed to a hungry lizard");
						/* Undo the move */
						board = UCI.copyBoard(previousBoard);
						int[] pos = pair(xLoc, zLoc);
						int[] oldPos = pair(oldX, oldZ);
						if (getPlayerAt(pos) != 0){
							buildPiece(pos, getPieceAt(pos), pBlocks[getPlayerAt(pos)], getPlayerAt(pos));
						}else{
							erasePiece(pos);
						}
						buildPiece(oldPos, getPieceAt(oldPos), pBlocks[getPlayerAt(oldPos)], getPlayerAt(oldPos));
						
						mc.postToChat("Illegal move!");
						clearTiles();
						availableMoves.clear();
					}
					
				}

			}
		}
	}
	
	/* Gets the color of wool on the board at a location (0-7) */
	public Color getBoardColor(int[] loc){
		Color replaceColor;
		try{
			replaceColor = colors[loc[0]*5][loc[1]*5];
		}catch (Exception e){
			e.printStackTrace();
			replaceColor = Color.BLACK;	
		}
		return replaceColor;
	}
	
	private boolean isAvailable(int[] loc){
		boolean rBool = false;
		for (int[] ia : availableMoves){
			if (ia[0] == loc[0] && ia[1] == loc[1]){
				rBool = true;
			}
		}
		return rBool;
	}
	
	public void movePiece(Move move){
		int[] from = {move.startX, move.startY};
		int[] to = {move.endX, move.endY};
		
		/* Deal with the last move highlights */
		if (lastMoveFrom == null){
			lastMoveFrom = from.clone();
			lastMoveTo = to.clone();
			drawOutline(lastMoveFrom, lastMoveBlock);
			drawOutline(lastMoveTo, lastMoveBlock);
		}else{
			drawOutline(lastMoveFrom, Block.wool(getBoardColor(lastMoveFrom)));
			drawOutline(lastMoveTo, Block.wool(getBoardColor(lastMoveTo)));
			lastMoveFrom = from.clone();
			lastMoveTo = to.clone();
			drawOutline(lastMoveFrom, lastMoveBlock);
			drawOutline(lastMoveTo, lastMoveBlock);
		}
		
		
		Piece mPiece = getPieceAt(from);
		int mPlayer = getPlayerAt(from);
		
		if (mPlayer == 0){
			mc.postToChat("Moved an empty space!");
		}
		
		erasePiece(from);
		if (getPieceAt(to) != empty){
			erasePiece(to);
		}
		buildPiece(to, mPiece, pBlocks[mPlayer], mPlayer);
		board[from[1]][from[0]] = "00";
		board[to[1]][to[0]] = getPieceStr(mPlayer, mPiece);
	}
	
	private String getPieceStr(int player, Piece piece){
		String rString = "";
		rString += player;
		rString += piece.getNum();
		return rString;
	}
	
	public static void main(String[] args) 
	{ 
		//Start the GUI
		new GUI();
	}
	
	public void start(Minecraft mcraft){		/*TODO Program begins here!*/
		/* Initialize UCI */
		uci = new UCI();
		/* Set the Minecraft world */
		mc = mcraft;
		/* Set a listener on our UCI object */
		uci.setOnChessEventListener(new UCI.ChessEventListener() 
		{
			@Override
			public void onGameOver() 
			{
				mc.postToChat("Game over!");
			}
			@Override
			public void onCheck() 
			{
				mc.postToChat("Check!");
			}
		});
		
		drawnTiles = new ArrayList<int[]>();		//Arraylist used to track locations of tiles
		availableMoves = new ArrayList<int[]>();		//Arraylist used to track available move locations
		
		
		colors = new Color[40][40];						//Create an array that contains the color pattern on the board
		for (int colx = 0; colx <= 39; colx++){			//define said array
			for (int coly = 0; coly <= 39; coly++){
				if ((coly>-1 && coly<5) || (coly>9 && coly<15) || (coly>19 && coly<25) || (coly>29 && coly<35)){
					if ((colx>-1 && colx<5) || (colx>9 && colx<15) || (colx>19 && colx<25) || (colx>29 && colx<35)){
						colors[colx][coly] = Color.WHITE;
					}
					if ((colx>4 && colx<10) || (colx>14 && colx<20) || (colx>24 && colx<30) || (colx>34 && colx<40)){
						colors[colx][coly] = Color.BLACK;
					}
				}
				if ((coly>4 && coly<10) || (coly>14 && coly<20) || (coly>24 && coly<30) || (coly>34 && coly<40)){
					if ((colx>-1 && colx<5) || (colx>9 && colx<15) || (colx>19 && colx<25) || (colx>
					29 && colx<35)){
						colors[colx][coly] = Color.BLACK;
					}   
					if ((colx>4 && colx<10) || (colx>14 && colx<20) || (colx>24 && colx<30) || (colx>34 && colx<40)){
						colors[colx][coly] = Color.WHITE;
					}
				}
			}
		}

		playerpos = mc.player.getPosition();
		origX = px-1;		//Origin location of the board
		origY = py-1;
		origZ = pz-1;
		boardExists = (mc.getBlock(xyz(0,-55,0)).equals(Block.DIAMOND_BLOCK));	//test for indicator block
		
		empty = new Empty(this);			//Initialize all of the pieces
		pawn = new Pawn(this);
		rook = new Rook(this);
		knight = new Knight(this);
		bishop = new Bishop(this);
		queen = new Queen(this);
		king = new King(this);
		if (boardExists){
		mc.postToChat("Board detected");
		}
		if (!boardExists){
			mc.postToChat("Board creation in progress...");
			/* Create the board */
			
			for (int cx = px-2; cx <= px+39; cx++){					
				for (int cz = pz-2; cz <= pz+39; cz++){
					if (cx == px-2 || cx == px+39 || cz == pz-2 || cz == pz+39){
						mc.setBlock(cx, py-1, cz, Block.GOLD_BLOCK);
					}else{
						mc.setBlock(cx, py-1, cz, Block.wool(colors[cx-px+1][cz-pz+1]));
					}
					if (makeCeiling){
						mc.setBlock(cx, py+CAGEHEIGHT+1, cz, Block.GLASS);
					}
					for (int cy = py; cy <= py+CAGEHEIGHT; cy++){
						if (cx == px-2 || cx == px+39 || cz == pz-2 || cz == pz+39){
							if (makeWalls){
								mc.setBlock(cx, cy, cz, Block.GLASS);
							}
						}else{
							mc.setBlock(cx, cy, cz, Block.AIR);
						}
					}
				}
			}
			mc.setBlock(xyz(0,-55,0), Block.DIAMOND_BLOCK);		//set an indicator block so the program knows there is a board
			mc.postToChat("Board creation complete! Building pieces");
		}else{
			clearBoard();
		}
		
		setUpPieces();
		
		drawOutline(pair(0,0),Block.GOLD_BLOCK);
		
		mc.postToChat("Piece creation complete. The game has now begun!");
		
		mc.player.setPosition(xyz(origX,py+5,origZ));
		clickEnabled = true;
		 listener = new HitListener(mc);
			listener.setOnBlockHitListener(new HitListener.OnBlockHitListener() {
				
				@Override
				public void onBlockHit(BlockHitEvent event, Vec position, Block b){ 
					System.out.println(b.toString());
					if (clickEnabled){
						doSelect(event);
					}
				}
			});
			
			listener.start();
	}
	
	public void clearBoard(){
		mc.postToChat("Clearing Board");
		for (int cx = px-2; cx <= px+39; cx++){					
			for (int cz = pz-2; cz <= pz+39; cz++){
				if (!(cx == px-2 || cx == px+39 || cz == pz-2 || cz == pz+39)){
					mc.setBlock(cx, py-1, cz, Block.AIR);
					mc.setBlock(cx, py-1, cz, Block.wool(colors[cx-px+1][cz-pz+1]));
				}
			}
		}
	mc.postToChat("Board cleared! Reseting pieces");
	}
	
	public void setUpPieces(){
		for (int piecex = 0; piecex<=7; piecex++){		//build pieces
			for (int piecez = 0; piecez<=7; piecez++){
				if (boardExists){
					erasePiece(pair(piecex, piecez));
				}
				int player = this.getPlayerAt(pair(piecex, piecez));
				Piece ptype = this.getPieceAt(pair(piecex, piecez));
				Block btype;
				if (player == 1){
					btype = p1Block;
				}else{
					btype = p2Block;
				}
				if (player != 0)
					buildPiece(pair(piecex, piecez), ptype, btype, player);
			}
		}
	}
	
	public void restart(){
		clickEnabled = false;
		String[][] b = {
				{"12","13","14","15","16","14","13","12"},
				{"11","11","11","11","11","11","11","11"},
				{"00","00","00","00","00","00","00","00"},
				{"00","00","00","00","00","00","00","00"},
				{"00","00","00","00","00","00","00","00"},
				{"00","00","00","00","00","00","00","00"},
				{"21","21","21","21","21","21","21","21"},
				{"22","23","24","25","26","24","23","22"},
		};
		board = b;
		clearBoard();
		setUpPieces();
		uci.startNewGame();
		drawOutline(pair(0,0),Block.GOLD_BLOCK);
		mc.postToChat("Piece creation complete. The game has now begun!");
		mc.player.setPosition(xyz(origX,py+5,origZ));
		clickEnabled = true;
	}
}
